package com.ylz.sjzx.sio.common.core.core.container.common;

import cn.hutool.core.util.StrUtil;
import com.ylz.sjzx.sio.common.core.context.ApplicationUtils;
import com.ylz.sjzx.sio.common.core.exception.PreParamException;
import org.apache.commons.lang3.ClassUtils;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.util.SystemPropertyUtils;

import java.io.File;
import java.io.FileFilter;
import java.net.URL;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * Description:基础容器扫描类、提供功能如下
 * 1、懒加载配置文件夹下所有class
 * 2、统一提供扫描方法
 */
public class BaseScanner {

    /**
     * class缓存
     */
    private static Set<Class<?>> classFiles = new HashSet<>();

    /**
     * 初始化加载文件
     *
     * @throws ClassNotFoundException 如果需要加载的class文件在文件目录下不存在时,会抛出此异常
     */
    private static void initFiles() throws ClassNotFoundException {

        synchronized (BaseScanner.class) {

            if (!classFiles.isEmpty()) {
                return;
            }

            String[] strings = getsourceRoot();
            if (strings == null) {
                return;
            }
            for (String appRoot : strings) {

                String appPath = appRoot.replace(".", "/");
                ClassLoader loader = Thread.currentThread().getContextClassLoader();
                URL appUrl = loader.getResource(appPath);
                if (appUrl == null) {
                    throw new PreParamException("路径 '" + appPath + "' 未找到或当前用户没有访问权限");
                }

                initFilesLoop(new File(appUrl.getFile()), appRoot);
            }

        }
    }

    /**
     * 搜索迭代方法
     *
     * @param file        被搜索文件对象
     * @param packageName 当前包路径
     * @throws ClassNotFoundException 如果需要加载的class文件在文件目录下不存在时,会抛出此异常
     */
    private static void initFilesLoop(File file, String packageName) throws ClassNotFoundException {
        if (!file.exists()) {
            return;
        }

        if (file.isDirectory()) {
            File[] subFiles = file.listFiles(new FileFilter() {
                @Override
                public boolean accept(File pathname) {
                    return pathname.isDirectory() || pathname.getName().endsWith(".class");
                }
            });

            if (subFiles == null) {
                return;
            }

            for (File subFile : subFiles) {
                String subPackageName = packageName;
                if (subFile.isDirectory()) {
                    subPackageName += "." + subFile.getName();
                }
                initFilesLoop(subFile, subPackageName);
            }
        } else {
            String className = file.getName().replace(".class", "");
            ClassLoader loader = Thread.currentThread().getContextClassLoader();
            classFiles.add(loader.loadClass(packageName + "." + className));
        }
    }

    /**
     * 搜索指定接口的实现类
     *
     * @param classObj 只能是接口的class.
     * @return Set
     */
    protected static Set<Class<?>> scan(Class<?> classObj) {

        try {
            if (classFiles.isEmpty()) {
                newInitClass();
            }

            Set<Class<?>> resultSet = new HashSet<>();
            for (Class clazz : classFiles) {
                List<Class<?>> collect = ClassUtils.getAllInterfaces(clazz);
                if (collect.contains(classObj)) {
                    resultSet.add(clazz);
                }
            }
            return resultSet;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 新写的方法，使用与springboot 打成jar包部署时，加载不到class的bug
     * 使用spring的PathMatchingResourcePatternResolver 来匹配路径
     * 2019-8-20  liuxw
     *
     * @throws Exception
     */
    private static void newInitClass() throws Exception {

        synchronized (BaseScanner.class) {

            if (!classFiles.isEmpty()) {
                return;
            }

            String[] appRootArr = getsourceRoot();
            if (appRootArr == null) {
                return;
            }

            for (String appRoot : appRootArr) {
                String appPath = appRoot.replace(".", "/");
                String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
                        + org.springframework.util.ClassUtils
                        .convertClassNameToResourcePath(SystemPropertyUtils.resolvePlaceholders(appPath))
                        + "/**/*.class";

                ResourcePatternResolver resourceLoader = new PathMatchingResourcePatternResolver();
                Resource[] source = resourceLoader.getResources(packageSearchPath);

                MetadataReaderFactory readerFactory = new CachingMetadataReaderFactory(resourceLoader);

                for (Resource resource : source) {
                    if (resource.isReadable()) {
                        MetadataReader reader = readerFactory.getMetadataReader(resource);
                        String className = reader.getClassMetadata().getClassName();
                        classFiles.add(Class.forName(className));
                    }
                }
            }

        }

    }

    private static String[] getsourceRoot() {
        String appRoot = ApplicationUtils.getProperty("base.adapter.root");
        if (StrUtil.isBlank(appRoot)) {
            return null;
        }

        return appRoot.split(",");
    }

    /**
     * 销毁对象
     * 慎重使用，一定到加载结束时候在调用
     */
    public static void destroyFilesCache() {
        classFiles = new HashSet<>();
    }
}
